
{-# OPTIONS -fglasgow-exts #-}

module Data.Transform.TypeHUnit where

import HUnit

import Data.Transform.Type
import Data.Set as Set
import Data.Map as Map
-- import Data.Transform.Rewriting (rmConstraints)

-----------------------------------------------------------------------------

main = runTestTT . TestList . Prelude.map TestCase $ 
    [ testSpineTInv
    , testSpineTInvWithRmConstraints
    -- , testImdb
    , testEvalMaybe
--    , testEvalSetIncTrue -- Fails!!
--    , testEvalSetIncFalse -- Fails!!
--    , testEvalSetIncEqSets -- Fails!!
    , testEvalDomEmpty
    , testEvalDom
    , testEvalRngEmpty
    , testEvalRng
    , testEvalRngRepeatedValues
    , testEvalMapJoin1Empty
    , testEvalMapJoin1SingleElement
    , testEvalMapJoin1SingleDomainMultipleRange
    ,testEvalMapJoin2
    ,testEvalMapJoin2Empty
    ,testEvalMapUnJoin2
    ,testEvalMapJoin2Empty
    ,testEvalJoin2UnJoin2
    ,testEvalUnJoin2Join2
    ,testEvalMapUnJoin1Join1
    ,testEvalMapJoin1UnJoin1
    ,testEvalJoin1UnJoin1Empty
    ,testEvalUnJoin1Join1Empty
    ]
    

testEvalMapJoin1Empty = do
   let value    = (Map.empty, Map.empty) :: (Map Int (), Map (Int,Int) Int)
   let expected =  Map.empty :: (Map Int (Map Int Int))
   let result   = eval MapJoin1 value
   assertEqual "Both maps should be empty" expected result
   
testEvalMapJoin1SingleElement = do
   let value    = (Map.fromList [(3,())], Map.fromList [((3,4), 5)])
   let expected = Map.fromList [(3, Map.fromList [(4,5)])]
   let result   = eval MapJoin1 value
   assertEqual "Maps should be equal" expected result

testEvalMapJoin1SingleDomainMultipleRange = do
   let value    = (Map.fromList [(3,())], Map.fromList [((3,4),5), ((3,5),6)])
   let expected = Map.fromList [(3, Map.fromList [(4,5), (5,6)])]
   assertEqual "Maps shall be equal" expected (eval MapJoin1 value)

testEvalMapUnJoin1Join1 = do
 let a = [(1,[("a",True)]),(2,[("b",False)]), (4,[("c",True)]), (8,[("d",False)]),(5,[("e",True)]), (6,[("f",False)])]
 let b = Map.fromDistinctAscList (Prelude.map (id -><-Map.fromDistinctAscList) a )
 let c = eval MapUnJoin1 b
 let e = eval MapJoin1 c
 assertBool "maps don't match" (b==e)

testEvalMapJoin1UnJoin1 = do
 let a = (Map.fromList [(1,()),(2,()),(4,()),(8,()),(5,()),(6,())],Map.fromList [((1,"a"),True),((2,"b"),False),((4,"c"),True),((8,"d"),False),((5,"e"),True),((6,"f"),False)]) 
 let b = eval MapJoin1 a
 let c = eval MapUnJoin1 b
 assertEqual  "map's don't match" a c

testEvalUnJoin1Join1Empty = do
 let a = Map.empty::  Map Int (Map Char Bool)
 let b = eval MapUnJoin1 a
 let c = eval MapJoin1 b
 assertEqual "map's don't match" a c

testEvalJoin1UnJoin1Empty = do
 let m1 = Map.empty ::Map Int One
 let m2 = Map.empty:: Map (Int, Char) Bool
 let m3 = (m1,m2)
 let m4 = eval MapJoin1 m3
 let m5 = eval MapUnJoin1 m4
 assertEqual "map's don't match" m3 m5

testEvalMapJoin2 = do     
 let m2  = Map.fromDistinctAscList (Prelude.map (id-><-(id-><-Map.fromDistinctAscList)) [(1,([1],[('a',['a'])])),(2,([2],[('b',['b'])])),(3,([3],[('c',['c'])])),(4,([4],[('d',['d'])]))])
 let m1' = Map.fromDistinctAscList [(1,[1]),(2,[2]),(3,[3]),(4,[4])]
 let m1''= Map.fromDistinctAscList [((1,'a'),['a']),((2,'b'),['b']),((3,'c'),['c']),((4,'d'),['d'])] 
 let m1  = (m1',m1'') 
 assertEqual "MapJoin2 m1 should be equal to m2" m2 (eval MapJoin2 m1)  

testEvalMapUnJoin2 = do  
 let m2' = Map.fromDistinctAscList [(1,[1]),(2,[2]),(3,[3]),(4,[4])] 
 let m2''= Map.fromDistinctAscList [((1,'a'),['a']),((2,'b'),['b']),((3,'c'),['c']),((4,'d'),['d'])]
 let m2  = (m2',m2'')
 let m1  = Map.fromDistinctAscList (Prelude.map (id-><-(id-><-Map.fromDistinctAscList)) [(1,([1],[('a',['a'])])),(2,([2],[('b',['b'])])),(3,([3],[('c',['c'])])),(4,([4],[('d',['d'])]))])  
 assertEqual "MapUnJoin2 m1 should be equal to m2" m2 (eval MapUnJoin2 m1)   


testEvalMapJoin2Empty = do 
 let m2 = Map.empty :: Map Int ([Char] , Map Int [Bool])
 let m1' = Map.empty :: Map Int [Char]
 let m1''= Map.empty::Map (Int,Int) [Bool]  
 let m1 = (m1',m1'')
 assertEqual "MapJoin2 m1 should be equal to m2" m2 (eval MapJoin2 m1)  


testEvalMapUnJoin2Empty = do 
 let m1 = Map.empty :: Map Int ([Char] , Map Int [Bool])
 let m2' = Map.empty :: Map Int [Char]
 let m2''= Map.empty::Map (Int,Int) [Bool]
 let m2 = (m2',m2'')
 assertEqual "MapUnJoin2 m1 should be equal to m2" m2 (eval MapUnJoin2 m1)  

testEvalJoin2UnJoin2 = do
 let m1  = Map.fromDistinctAscList (Prelude.map (id-><-(id-><-Map.fromDistinctAscList)) [(1,([1],[('a',['a'])])),(2,([2],[('b',['b'])])),(3,([3],[('c',['c'])])),(4,([4],[('d',['d'])]))]) 
 assertEqual "MapJoin2 (MapUnJoin2 m1) should be equal to m1" m1 (eval MapJoin2 (eval MapUnJoin2 m1))  

testEvalUnJoin2Join2 = do   
 let m1' = Map.fromDistinctAscList [(1,[1]),(2,[2]),(3,[3]),(4,[4])] 
 let m1''= Map.fromDistinctAscList [((1,'a'),['a']),((2,'b'),['b']),((3,'c'),['c']),((4,'d'),['d'])] 
 let m1  = (m1',m1'')
 assertEqual "MapUnJoin2 (MapJoin2 m1) should be equal to m1" m1 (eval MapUnJoin2 (eval MapJoin2 m1))


testSpineTInv = do
   let t = PF (Func (TInv (Set Int) (FUN "" (const True))) (Set Int))
   let r = toSpine t (SET ID)
   assertBool "" ((fromSpine r) == (fromSpine r))
   
testSpineTInvWithRmConstraints = do
   let f = SET ID
   let t = PF (Func (TInv (Set Int) (FUN "inv" (const True))) (Set Int))
   let r = toSpine (rmConstraints t) f
   assertBool "Should not throw exception" (fromSpine r == fromSpine r)
   assertEqual "Should reconstruct original" (SET ID) (fromSpine r)


testEvalMaybe = do
   let f = MAYBE (FUN "showInt" (\ (x :: Int) -> show x))
   assertEqual "Result value should be 'Nothing'" Nothing (eval f Nothing)
   assertEqual "Result value should be applied to Just x" (Just "1") (eval f (Just 1))
   
testEvalSetIncTrue = do
   let smallerSet = FUN "small set" (const (Set.fromList [1,2,3]))
   let largerSet  = FUN "large set" (const (Set.fromList [0,1,2,3,4,5]))
   let test = SetInc Int smallerSet largerSet
   assertBool "Smaller set should be contained in the larger set" (eval test 1)
   
testEvalSetIncFalse = do
   let smallerSet = FUN "small set" (const (Set.fromList [1,2,3]))
   let largerSet  = FUN "large set" (const (Set.fromList [0,1,2,3,4,5]))
   let test = SetInc Int largerSet smallerSet
   assertBool "Larger set should not be contained in the smaller set" (not $ eval test 1)

testEvalSetIncEqSets = do
   let set = FUN "small set" (const (Set.fromList [1,2,3]))
   let test = SetInc Int set set
   assertBool "Larger set should not be contained in the smaller set" (eval test 1)

testEvalDomEmpty = do
   let set  = (Set.fromList []) :: Set Int
   let map' = Map.empty :: Map Int Int
   assertEqual "Domain should be equal to empty set" set (eval DOM map')
   
testEvalDom = do
   let set  = Set.fromList [1,2,3,3]
   let map' = Map.fromList [(1,1), (2,1), (3,1)]
   assertEqual "Map domains should be equal to set" set (eval DOM map')
   
testEvalRngEmpty = do
   let set  = Set.empty :: Set Int
   let map' = Map.empty :: Map Int Int
   assertEqual "Map range should be equal to empty set" set (eval RNG map')

testEvalRng = do
   let set  = Set.fromList [1,2,3]
   let map' = Map.fromList [(1,1), (2,2), (4,3)]
   assertEqual "Map range should be equal to set" set (eval RNG map')

testEvalRngRepeatedValues = do
   let set  = Set.fromList [1,2,3]
   let map' = Map.fromList [(1,1), (2,2), (3,2), (4,3)]
   assertEqual "Map range should be equal to set" set (eval RNG map')


